package com.appspot.diasporajava.dao;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.jdo.annotations.Transactional;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.PersistenceContext;
import javax.persistence.PersistenceUnit;
import javax.persistence.Query;

public class GenericDaoImpl<T> implements GenericDao<T> {

	EntityManagerFactory emf;

	protected Class<T> type;

	public GenericDaoImpl() {
		this.emf=EMF.get();
		//em = emf.createEntityManager();
		 Type t = getClass().getGenericSuperclass();
		 ParameterizedType pt = (ParameterizedType) t;
		 type = (Class) pt.getActualTypeArguments()[0];
	}
	
	

	@PersistenceUnit
	public void setEntityManagerFactory(EntityManagerFactory emf) {
		this.emf = emf;
	}

	@Override
	public long countAll(final Map params) {

		final StringBuffer queryString = new StringBuffer("SELECT count(o) from ");

		queryString.append(type.getSimpleName()).append(" o ");
		queryString.append(this.getQueryClauses(params, null));

		final Query query = this.emf.createEntityManager().createQuery(queryString.toString());

		return (Long) query.getSingleResult();

	}

	@Override
	@Transactional
	public T create(final T t) {
		EntityManager em = this.emf.createEntityManager();
		em.persist(t);
		em.close();
		return t;
	}

	@Override
	public void delete(final Object id) {
		this.emf.createEntityManager().remove(this.emf.createEntityManager().getReference(type, id));
	}

	@Override
	public T find(final Object id) {
		return (T) this.emf.createEntityManager().find(type, id);
	}

	@Override
	public T update(final T t) {
		return this.emf.createEntityManager().merge(t);
	}

	private String getQueryClauses(final Map<String, Object> params, final Map<String, Object> orderParams) {
		final StringBuffer queryString = new StringBuffer();
		if ((params != null) && !params.isEmpty()) {
			queryString.append(" where ");
			for (final Iterator<Map.Entry<String, Object>> it = params.entrySet().iterator(); it.hasNext();) {
				final Map.Entry<String, Object> entry = it.next();
				if (entry.getValue() instanceof Boolean) {
					queryString.append(entry.getKey()).append(" is ").append(entry.getValue()).append(" ");
				} else {
					if (entry.getValue() instanceof Number) {
						queryString.append(entry.getKey()).append(" = ").append(entry.getValue());
					} else {
						// string equality
						queryString.append(entry.getKey()).append(" = '").append(entry.getValue()).append("'");
					}
				}
				if (it.hasNext()) {
					queryString.append(" and ");
				}
			}
		}
		if ((orderParams != null) && !orderParams.isEmpty()) {
			queryString.append(" order by ");
			for (final Iterator<Map.Entry<String, Object>> it = orderParams.entrySet().iterator(); it.hasNext();) {
				final Map.Entry<String, Object> entry = it.next();
				queryString.append(entry.getKey()).append(" ");
				if (entry.getValue() != null) {
					queryString.append(entry.getValue());
				}
				if (it.hasNext()) {
					queryString.append(", ");
				}
			}
		}
		return queryString.toString();
	}



	@Override
	public List<T> list() {
		final StringBuffer queryString = new StringBuffer("SELECT o from ");

		queryString.append(type.getSimpleName()).append(" o ");

		final Query query = this.emf.createEntityManager().createQuery(queryString.toString());

		return query.getResultList();
	}

}